﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using CoreEx.Database.Client;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using CoreEx.Common.Extensions;
using LinFu.Reflection;

namespace CoreEx.Database.Query
{    
    public class Query : IQuery, IInitialize<IServiceRequestResult>
    {
        private IQueryExpression _expression;
        private IServiceContainer _serviceContainer;
        private IExpressionFactory _expressionFactory;
        private IQueryTranslator _queryTranslator;


       
        public SelectQueryExpression Select(params IExpression[] expressions)
        {
            IExpression projectionExpression = WrapExpressionArray(expressions);
            _expression = _serviceContainer.GetService<ISelectExpression>(projectionExpression, _expressionFactory);
            return (SelectQueryExpression)_expression;
               
        }

        public SelectQueryExpression Select(params Func<IExpressionFactory, IExpression>[] expressions)
        {
            return Select(expressions.Select(e => e(_expressionFactory)).ToArray());                        
        }

        public IUpdateQuery Update(ITableExpression tableExpression)
        {
            throw new System.NotImplementedException();
        }

        public IUpdateQuery Update(Func<IExpressionFactory, IExpression> tableExpression)
        {
            throw new System.NotImplementedException();
        }

       




        protected IQueryExpression Expression
        {
            get { return _expression; }
        }

        public IServiceContainer ServiceContainer
        {
            get { return _serviceContainer; }
        }


        protected IExpressionFactory ExpressionFactory
        {
            get { return _expressionFactory; }
        }

        public void Initialize(IServiceRequestResult source)
        {
            _serviceContainer = source.Container;
            _expressionFactory = _serviceContainer.GetService<IExpressionFactory>();
            var connectionInfo = _serviceContainer.GetService<IConnectionInfo>(source.ServiceName);
            _queryTranslator = _serviceContainer.GetService<IQueryTranslator>(connectionInfo.ProviderName);
        }

        protected IExpression WrapExpressionArray(IExpression[] expressions)
        {
            if (expressions == null || expressions.Length == 0)
                throw new ArgumentOutOfRangeException("expressions", "The expression list can not be null or empty");

            if (expressions.Length > 1)
                return ServiceContainer.GetService<IExpressionList>().AddRange(expressions);

            return expressions[0];
        }
    }

    public class QueryExpression<TQuery> : ExpressionBase,IQueryExpression<TQuery>,IInitialize
    {

        private IExpression _fromExpression;
        private IExpression _whereExpression;
        private IExpressionFactory _expressionFactory;
        protected IServiceContainer _serviceContainer;
        

        public QueryExpression(IExpressionFactory expressionFactory)
        {
            _expressionFactory = expressionFactory;
        }

        public TQuery From(params Func<IExpressionFactory, IExpression>[] expressions)
        {
            return From(expressions.Select(e => e(_expressionFactory)).ToArray());                        
        }

        public TQuery From(params IExpression[] expressions)
        {
            _fromExpression = expressions.WrapExpressionList();
            return (TQuery)(IQueryExpression<TQuery>)this;
            
        }

        
        
        
        public TQuery InnerJoin(IExpression target, IExpression expression)
        {
            var joinExpression = _serviceContainer.GetService<IJoinExpression>();
            joinExpression.JoinType = JoinType.Inner;
            joinExpression.Target = target;
            joinExpression.Expression = expression;

            var compositeExpression = _serviceContainer.GetService<ICompositeExpression>();
            compositeExpression.LeftExpression = _fromExpression;
            compositeExpression.RightExpression = joinExpression;
            _fromExpression = compositeExpression;

            return (TQuery)(IQueryExpression<TQuery>)this;
        }

        public TQuery InnerJoin(Func<IExpressionFactory, IExpression> target, Func<IExpressionFactory, IExpression> expression)
        {
            return InnerJoin(target(_expressionFactory), expression(_expressionFactory));
        }


        public TQuery LeftOuterJoin(IExpression target, IExpression expression)
        {
            throw new System.NotImplementedException();
        }

        public TQuery RightOuterJoin(IExpression target, IExpression expression)
        {
            throw new System.NotImplementedException();
        }




        public TQuery Where(Func<IExpressionFactory, IExpression> expression)
        {
            return Where(expression(_expressionFactory));            
        }

        public TQuery Where(IExpression expression)
        {
            if (_whereExpression != null)
                _whereExpression = ExpressionFactory.And(_whereExpression, expression);
            else
                _whereExpression = expression;
            return (TQuery)(IQueryExpression<TQuery>)this;
        }

        public IExpressionFactory ExpressionFactory
        {
            get { return _expressionFactory; }
            set { _expressionFactory = value; }
        }

       

        IExpression IQueryExpression.FromExpression
        {
            get { return _fromExpression; }

            set { _fromExpression = value; }
        }

        IExpression IQueryExpression.WhereExpression
        {
            get { return _whereExpression; }            
        }


        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}